/*
  Copyright (c) 2010 Les Newell. All rights reserved

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/



#include "wx_pch.h"
#include "MainFrame.h"
#include "ConfigDialog.h"

#ifndef WX_PRECOMP
//(*InternalHeadersPCH(MainFrame)
//*)
#endif
//(*InternalHeaders(MainFrame)
#include <wx/xrc/xmlres.h>
//*)

//(*IdInit(MainFrame)
//*)

BEGIN_EVENT_TABLE(MainFrame,wxFrame)
    //(*EventTable(MainFrame)
    //*)
END_EVENT_TABLE()

MainFrame::MainFrame(wxWindow* parent)
{
    wxConfigBase * cfg = wxConfigBase::Get();
    m_curModule = NULL;
    //(*Initialize(MainFrame)
    wxXmlResource::Get()->LoadObject(this,parent,_T("MainFrame"),_T("wxFrame"));
    lbModules = (wxListBox*)FindWindow(XRCID("ID_LISTBOX1"));
    swSignals = (wxScrolledWindow*)FindWindow(XRCID("ID_SCROLLEDWINDOW1"));
    splitterModules = (wxSplitterWindow*)FindWindow(XRCID("ID_SPLITTERWINDOW1"));
    txtLog = (wxTextCtrl*)FindWindow(XRCID("ID_TEXTCTRL1"));
    txtCommand = (wxTextCtrl*)FindWindow(XRCID("ID_COMMAND"));
    splitterLog = (wxSplitterWindow*)FindWindow(XRCID("ID_SPLITTERWINDOW2"));
    btnConnect = (wxButton*)FindWindow(XRCID("ID_BUTTON1"));
    mnuSave = (wxMenuItem*)FindWindow(XRCID("wxID_SAVE"));
    mnuGetModules = (wxMenuItem*)FindWindow(XRCID("ID_GETMODULES"));
    mnuPort = (wxMenuItem*)FindWindow(XRCID("ID_MENUITEM1"));

    Connect(XRCID("ID_LISTBOX1"),wxEVT_COMMAND_LISTBOX_SELECTED,(wxObjectEventFunction)&MainFrame::OnlbModulesSelect);
    Connect(XRCID("ID_BUTTON1"),wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&MainFrame::OnbtnConnectClick);
    Connect(XRCID("wxID_OPEN"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuLoadSelected);
    Connect(XRCID("wxID_SAVE"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuSaveSelected);
    Connect(XRCID("idMenuQuit"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuQuitSelected);
    Connect(XRCID("ID_GETMODULES"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuGetModulesSelected);
    Connect(XRCID("ID_WRITE"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuWriteSelected);
    Connect(XRCID("ID_MENUITEM1"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuPortSelected);
    Connect(XRCID("idMenuAbout"),wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnmnuAboutSelected);
    //*)


    txtCommand->Connect(wxEVT_KEY_DOWN,(wxObjectEventFunction)&MainFrame::OntxtCommandKey,NULL,this);
    m_serial = new Serial(this);
    Connect(wxID_ANY,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&MainFrame::OnmonitorClick);
    Connect(wxID_ANY,wxEVT_COMMAND_TEXT_ENTER,(wxObjectEventFunction)&MainFrame::OnsigValueTextEnter);
    Connect(wxID_ANY,wxEVT_LIST_RECEIVED,(wxObjectEventFunction)&MainFrame::OnList);
    Connect(wxID_ANY,wxEVT_LOG,(wxObjectEventFunction)&MainFrame::OnLog);
    Connect(wxID_ANY,wxEVT_CONNECT,(wxObjectEventFunction)&MainFrame::OnConnect);
    m_log = new wxLogTextCtrl(txtLog);
    wxLog::SetActiveTarget(m_log);

    cfg->SetPath(_T("/MainWindow"));
    SetSize(cfg->Read(_T("X"),0l),
            cfg->Read(_T("Y"),0l),
            cfg->Read(_T("W"),600l),
            cfg->Read(_T("H"),400l));
    cfg->SetPath(_T("/Sashes"));
    double g = 0;
    cfg->Read(_T("Modules"),&g);
    splitterModules->SetSashGravity(g);
    cfg->Read(_T("Log"),&g);
    splitterLog->SetSashGravity(g);
    swSignals->SetScrollRate(1,1);

    cfg->SetPath(_T("/misc"));
    m_compact = true;
    cfg->Read(_T("CompactData"),&m_compact);
}

MainFrame::~MainFrame()
{
    wxConfigBase * cfg = wxConfigBase::Get();
    cfg->SetPath(_T("/MainWindow"));
    wxRect rct = GetRect();
    if(!IsMaximized() && !IsIconized())
    {
        cfg->Write(_T("X"),rct.x);
        cfg->Write(_T("Y"),rct.y);
        cfg->Write(_T("W"),rct.width);
        cfg->Write(_T("H"),rct.height);
    }
    cfg->SetPath(_T("/Sashes"));
    cfg->Write(_T("Modules"),splitterModules->GetSashGravity());
    cfg->Write(_T("Log"),splitterLog->GetSashGravity());


    wxLog::SetActiveTarget(NULL);
    delete m_serial;
    delete m_log;
    //(*Destroy(MainFrame)
    //*)
}



void MainFrame::OnmnuGetModulesSelected(wxCommandEvent& event)
{
    if (!m_data.Refresh(m_serial))
    {
        return;
    }
    m_data.ListModules(lbModules);
//    GetSizer()->Fit(this);
    m_curModule = NULL;
    swSignals->DestroyChildren();
}

void MainFrame::OnmnuWriteSelected(wxCommandEvent& event)
{
    if (!m_serial->WriteEEPROM())
    {
        wxMessageBox(_("Write failed"),_("Write to EEPROM"),wxICON_ERROR);
    }
}

void MainFrame::OnmnuLoadSelected(wxCommandEvent& event)
{
    wxFileDialog dlg(NULL,_("Load file"),_T(""),_T(""),_T("EEPROM files(*.eep)|*.eep"),wxFD_OPEN | wxFD_FILE_MUST_EXIST);
    if (dlg.ShowModal() != wxID_OK)
    {
        return;
    }
    wxFFile fil(dlg.GetPath());
    if (!fil.IsOpened())
    {
        wxMessageBox(_("File error"));
        return;
    }
    wxString txt;
    fil.ReadAll(&txt);
    wxStringTokenizer tok(txt,_T("\n"));
    m_serial->ClearList();
    while (tok.HasMoreTokens())
    {
        wxString s = tok.GetNextToken();
        m_serial->SetValue(s);
    }
}

void MainFrame::OnmnuSaveSelected(wxCommandEvent& event)
{
    wxFileDialog dlg(NULL,_("Save file"),_T(""),_T(""),_T("EEPROM files(*.eep)|*.eep"),wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
    if (dlg.ShowModal() != wxID_OK)
    {
        return;
    }
    if (!m_data.Save(dlg.GetPath(),m_serial))
    {
        wxMessageBox(_("Error saving file"), _("File error"), wxICON_ERROR);
    }
}

void MainFrame::OnlbModulesSelect(wxCommandEvent& event)
{
    int idx = event.GetInt();
    ShowModule(idx);
}

void MainFrame::ShowModule(int idx)
{
    m_serial->ClearList();
    if(idx >= m_data.size())
    {
        wxLogMessage(_T("Modules and list out of sync"));
        return;
    }
    Freeze();
    m_curModule = NULL;
    Module &mod = m_data[idx];
    wxWindow * win = swSignals;
    win->DestroyChildren();
    wxFlexGridSizer* siz = new wxFlexGridSizer(0, 3, wxDLG_UNIT(swSignals,wxSize(3,0)).GetWidth(), wxDLG_UNIT(win,wxSize(3,0)).GetWidth());
    for (int ct=0; ct< mod.signals.size(); ct++)
    {
        Signal &sig = mod.signals[ct];
        m_serial->GetValue(sig.GetId(m_compact), sig.value);
        sig.label = new wxStaticText(win, wxID_ANY, sig.name);
        siz->Add(sig.label, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
        if (sig.canWrite)
        {
            sig.textCtrl = new wxTextCtrl(win, ct, sig.ToString(),wxDefaultPosition,wxDefaultSize,wxTE_RIGHT);
            sig.textCtrl->Connect(wxID_ANY,wxEVT_KILL_FOCUS,(wxObjectEventFunction)&MainFrame::OnsigValueTextEnter,NULL,this);
        }
        else
        {
            sig.textCtrl = new wxTextCtrl(win, ct, sig.ToString(),wxDefaultPosition,wxDefaultSize,wxTE_READONLY | wxTE_RIGHT);
        }
        siz->Add(sig.textCtrl, 0, wxEXPAND|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 0);
        sig.monitor = new wxCheckBox(win, ct, _("Monitor"));
        sig.monitor->Enable(!sig.canWrite);
        siz->Add(sig.monitor, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
    }
    win->SetSizer(siz);
    win->FitInside();
    m_curModule = &mod;
    Thaw();
}

void MainFrame::OnmonitorClick(wxCommandEvent& event)
{
    if (m_curModule == NULL)
    {
        return;
    }
    wxArrayString list;
    for (int ct=0; ct< m_curModule->signals.size(); ct++)
    {
        Signal &sig = m_curModule->signals[ct];
        if (sig.monitor->GetValue())
        {
            list.Add(sig.GetId(m_compact));
        }
    }
    m_serial->RunList(list);
}

void MainFrame::OnList(wxStringEvent& evt)
{
    if (!m_curModule)
    {
        return;
    }
    wxStringTokenizer tok(evt.GetString(),_T(","));
    for (int ct=0; ct< m_curModule->signals.size(); ct++)
    {
        Signal &sig = m_curModule->signals[ct];
        if (sig.monitor->GetValue())
        {
            wxString numb = tok.GetNextToken();
            if (numb.IsEmpty())
            {
                wxLogMessage(_T("Numbers out of sync"));
                break;
            }
            numb.ToDouble(&sig.value);
            sig.textCtrl->SetValue(sig.ToString());
        }
    }
}

void MainFrame::OnsigValueTextEnter(wxCommandEvent& event)
{
    if (!m_curModule)
    {
        return;
    }
    int id = event.GetId();
    if (id<0 || id >= m_curModule->signals.size())
    {
        return;
    }
    Signal &sig = m_curModule->signals[id];
    wxString txt = sig.textCtrl->GetValue();
    wxString sigId = sig.GetId(m_compact);

    double res=0;
    if (!sig.canWrite)
    {
        return;
    }
    if (txt.ToDouble(&res))
    {
        if (res == sig.value)
        {
            return;
        }
        double old = sig.value;
        sig.value = res;
        if (!m_serial->SetValue(sigId,sig.ToString()))
        {
            sig.value = old;
        }
    }

    m_serial->GetValue(sigId,sig.value);
//    m_serial->EnableList();
    sig.textCtrl->SetValue(sig.ToString());
}

void MainFrame::OnbtnConnectClick(wxCommandEvent& event)
{
    if (m_serial->IsOpen())
    {
        m_serial->Close();
    }
    else
    {
        m_serial->Open();
        wxString ver;
        if(m_serial->GetVersion(ver))
        {
            int idx = lbModules->GetSelection();
            if (ver != m_currentVersion)
            {
                OnmnuGetModulesSelected(event);
                m_currentVersion = ver;
            }
            if(idx == wxNOT_FOUND)
            {
                idx = 0;
            }
            if(idx >= lbModules->GetCount())
            {
                idx = 0;
            }
            if(lbModules->GetCount() > 0)
            {
                lbModules->SetSelection(idx);
                ShowModule(idx);
            }
            return;
        }
        wxMessageBox(_("Unable to get version"));
        m_serial->Close();
    }
}

void MainFrame::OnLog(wxStringEvent& evt)
{
    txtLog->AppendText(evt.GetString() + _T("\n"));
}

void MainFrame::OnConnect(wxCommandEvent& evt)
{
    bool state = evt.GetInt();
    txtCommand->Enable(state);
    if (state)
    {
        btnConnect->SetLabel(_("Disconnect"));
    }
    else
    {
        btnConnect->SetLabel(_("Connect"));
    }
}

void MainFrame::OnmnuQuitSelected(wxCommandEvent& event)
{
    Close();
}


void MainFrame::OnmnuAboutSelected(wxCommandEvent& event)
{
    wxString msg = _("FlightOS configurator\nVersion ");
    wxMessageBox(msg + _T(VERSION), _("Welcome to..."));
}


void MainFrame::OntxtCommandKey(wxKeyEvent& event)
{
    if (event.GetKeyCode() == WXK_RETURN)
    {
        m_serial->Send(txtCommand->GetValue());
    }
    event.Skip();
}



void MainFrame::OnmnuPortSelected(wxCommandEvent& event)
{
    ConfigDialog dlg;
    dlg.ShowModal();
}


